package com.example.hxds.mps.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.MapUtil;
import com.example.hxds.mps.service.DriverLocationService;
import com.example.hxds.mps.service.MapService;
import com.example.hxds.mps.util.CoordinateTransform;
import org.gavaghan.geodesy.Ellipsoid;
import org.gavaghan.geodesy.GeodeticCalculator;
import org.gavaghan.geodesy.GeodeticCurve;
import org.gavaghan.geodesy.GlobalCoordinates;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author XUYANG
 * @Date 2024-03-19 14:47
 */
@Service
public class DriverLocationServiceImpl implements DriverLocationService {
    @Resource
    private RedisTemplate redisTemplate;


    @Override
    public void updateLocationCache(Map param) {
        long driverId=MapUtil.getLong(param,"driverId");
        String latitude=MapUtil.getStr(param,"latitude");
        String longitude=MapUtil.getStr(param,"longitude");

        //接单范围
        int rangeDistance=MapUtil.getInt(param,"rangeDistance");
        int orderDistance=MapUtil.getInt(param,"orderDistance");
        //封装成Point对象缓存到Redis中
        Point point=new Point(Convert.toDouble(longitude),Convert.toDouble(latitude));
        redisTemplate.opsForGeo().add("driver_location",point,driverId+"");


        //定向接单
        String orientateLongitude=null;
        if (param.get("orientateLongitude")!=null){
            orientateLongitude=MapUtil.getStr(param,"orientateLongitude");
        }
        String orientateLatitude=null;
        if (param.get("orientateLatitude")!=null){
            orientateLatitude=MapUtil.getStr(param,"orientateLatitude");
        }
        String orientation="none";
        if (orientateLatitude!=null&&orientateLongitude!=null){
            orientation=orientateLatitude+","+orientateLongitude;
        }
        String tmp=rangeDistance+"#"+orderDistance+"#"+orientation;
        redisTemplate.opsForValue().set("driver_online#"+driverId,tmp,60, TimeUnit.MINUTES);
    }

    @Override
    public void removeLocationCache(long driverId) {
        redisTemplate.opsForGeo().remove("driver_location",driverId+"");
        redisTemplate.delete("driver_online#"+driverId);
    }

    @Override
    public ArrayList searchBefittingDriverAboutOrder(double startPlaceLatitude, double startPlaceLongitude, double endPlaceLatitude, double endPlaceLongitude, double mileage) {
        //搜索订单起始位置10km以内的司机
        Point point=new Point(startPlaceLongitude,startPlaceLatitude);
        Metric metric= RedisGeoCommands.DistanceUnit.KILOMETERS;
        Distance distance=new Distance(10,metric);
        Circle circle=new Circle(point,distance);
        RedisGeoCommands.GeoRadiusCommandArgs args=RedisGeoCommands.GeoRadiusCommandArgs
                .newGeoRadiusArgs()
                .includeDistance()//结果中包含距离
                .includeCoordinates()//结果中包含坐标
                .sortAscending();//升序
        GeoResults radius = redisTemplate.opsForGeo().radius("driver_location", circle, args);
        ArrayList list=new ArrayList();
        if (radius!=null){
            Iterator<GeoResult<RedisGeoCommands.GeoLocation<String>>> iterator = radius.iterator();
            while (iterator.hasNext()){
                GeoResult<RedisGeoCommands.GeoLocation<String>> results = iterator.next();
                RedisGeoCommands.GeoLocation<String> content = results.getContent();
                String driverId = content.getName();
                double dist = results.getDistance().getValue();
                if (!redisTemplate.hasKey("driver_online#"+driverId)){
                    continue;
                }
                Object obj = redisTemplate.opsForValue().get("driver_online#" + driverId);
                if (obj==null){
                    continue;
                }
                String value = obj.toString();
                String[] tmp = value.split("#");
                int rangeDistance=Integer.parseInt(tmp[0]);
                int orderDistance=Integer.parseInt(tmp[1]);
                String orientation = tmp[2];

                boolean match=(dist<=rangeDistance);
                boolean distance_match=false;
                if (orderDistance==0){
                    distance_match=true;
                }else if (orderDistance==5&&mileage>0&&mileage<=5){
                    distance_match=true;
                }else if (orderDistance==10&&mileage>5&&mileage<=10){
                    distance_match=true;
                }else if (orderDistance==15&&mileage>10&&mileage<=15){
                    distance_match=true;
                }else if (orderDistance==20&&mileage>15&&mileage<=20){
                    distance_match=true;
                }
                boolean orientation_match=false;
                if (!orientation.equals("none")){
                    double orientationLatitude=Double.parseDouble(orientation.split(",")[0]);
                    double orientationLongitude=Double.parseDouble(orientation.split(",")[1]);
                    double[] location = CoordinateTransform.transformBD09ToWGS84(orientationLongitude, orientationLatitude);
                    GlobalCoordinates point1=new GlobalCoordinates(location[1],location[0]);
                    location=CoordinateTransform.transformBD09ToWGS84(endPlaceLongitude, endPlaceLatitude);
                    GlobalCoordinates point2=new GlobalCoordinates(location[1],location[0]);
                    GeodeticCurve geodeticCurve=new GeodeticCalculator().calculateGeodeticCurve(Ellipsoid.WGS84,point1,point2);
                    if (geodeticCurve.getEllipsoidalDistance()<=3000){
                        orientation_match=true;
                    }
                }else {
                    orientation_match=true;
                }
                if (match&&orientation_match&&distance_match){
                    HashMap map=new HashMap(){
                        {
                            put("driverId",driverId);
                            put("distance",dist);
                        }

                    };
                    list.add(map);
                }
            }

        }
        return list;
    }

    @Override
    public void updateOrderLocationCache(Map param) {
        long orderId = MapUtil.getLong(param, "orderId");
        String latitude = MapUtil.getStr(param, "latitude");
        String longitude = MapUtil.getStr(param, "longitude");
        String location=latitude+"#"+longitude;
        redisTemplate.opsForValue().set("order_location#"+orderId,location,10,TimeUnit.MINUTES);

    }

    @Override
    public HashMap SearchOrderLocationCache(long orderId) {
        Object obj = redisTemplate.opsForValue().get("order_location#" + orderId);
        if (obj!=null){
            String[] tmp=obj.toString().split("#");
            String latitude=tmp[0];
            String longitude=tmp[1];
            HashMap map=new HashMap<>(){
                {
                    put("latitude",latitude);
                    put("longitude",longitude);
                }
            };
            return map;
        }
        return null;
    }
}
